/**
 * This file is part of 1genia trampoline
 * Copyright (C) 2007 1genia (contact@1genia.com)
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; version 3 of the License. 
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details. 
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; see the file COPYING.TXT.  If not,
 * write to the Free Software Foundation, Inc., 51 Franklin Street,
 * Fifth Floor, Boston, MA 02110-1301, USA. 
 **/
package com.genia.toolbox.web.gwt.basics.client.widget;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.genia.toolbox.web.gwt.basics.client.CssNames;
import com.genia.toolbox.web.gwt.basics.client.GwtHelper;
import com.genia.toolbox.web.gwt.basics.client.bean.GwtLocale;
import com.genia.toolbox.web.gwt.basics.client.i18n.GwtI18nMessage;
import com.genia.toolbox.web.gwt.basics.client.listener.CustomDelegatingChangeListenerCollection;
import com.genia.toolbox.web.gwt.basics.client.listener.CustomDelegatingClickListenerCollection;
import com.genia.toolbox.web.gwt.basics.client.listener.CustomDelegatingFocusListenerCollection;
import com.genia.toolbox.web.gwt.basics.client.listener.CustomDelegatingKeyboardListenerCollection;
import com.genia.toolbox.web.gwt.basics.client.message.Messages;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.ChangeListener;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FocusListener;
import com.google.gwt.user.client.ui.KeyboardListener;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.SourcesChangeEvents;
import com.google.gwt.user.client.ui.SourcesClickEvents;
import com.google.gwt.user.client.ui.SourcesFocusEvents;
import com.google.gwt.user.client.ui.SourcesKeyboardEvents;
import com.google.gwt.user.client.ui.TextBoxBase;

/**
 * base class for widgets that allow to enter text for different languages.
 */
public abstract class AbstractI18nTextBoxBase
    extends Composite
    implements SourcesKeyboardEvents, SourcesChangeEvents, SourcesClickEvents, SourcesFocusEvents
{

  /**
   * This method is called when a widget is attached to the browser's document.
   * To receive notification after a Widget has been added to the document,
   * override the {@link #onLoad} method.
   * 
   * @see com.google.gwt.user.client.ui.Composite#onAttach()
   */
  @Override
  protected void onAttach()
  {
    try {
      LocaleTabListener.LAZY_TAB_PANELS_TO_CHANGE.add(panel);
    }
    finally {
      super.onAttach();
    }
  }



  /**
   * This method is called when a widget is detached from the browser's
   * document. To receive notification before a Widget is removed from the
   * document, override the {@link #onUnload} method.
   * 
   * @see com.google.gwt.user.client.ui.Composite#onDetach()
   */
  @Override
  protected void onDetach()
  {
    try {
      LocaleTabListener.LAZY_TAB_PANELS_TO_CHANGE.remove(panel);
    }
    finally {
      super.onDetach();
    }
  }

  /**
   * represent the collection of available locale with corresponding
   * <code>TextBoxBase</code>.
   */
  private final transient Map<GwtLocale, TextBoxBase> widgetsByLocale = new HashMap<GwtLocale, TextBoxBase>();

  /**
   * the {@link CustomDelegatingKeyboardListenerCollection}.
   */
  private final transient CustomDelegatingKeyboardListenerCollection keyboardListenerCollection = new CustomDelegatingKeyboardListenerCollection(this);

  /**
   * the {@link CustomDelegatingChangeListenerCollection}.
   */
  private final transient CustomDelegatingChangeListenerCollection changeListenerCollection = new CustomDelegatingChangeListenerCollection(this);

  /**
   * {@link CustomDelegatingClickListenerCollection}.
   */
  private final transient CustomDelegatingClickListenerCollection clickListenerCollection = new CustomDelegatingClickListenerCollection(this);

  /**
   * {@link CustomDelegatingFocusListenerCollection}.
   */
  private final transient CustomDelegatingFocusListenerCollection focusListenerCollection = new CustomDelegatingFocusListenerCollection(this);

  /**
   * list of {@link Command}.
   */
  private final transient List<Command> pendingCommands = new ArrayList<Command>();

  /**
   * a boolean to manage commands.
   */
  private boolean initialized = false;

  /**
   * the {@link LazyTabPanel} containing the informations.
   */
  private final LazyTabPanel panel = new LazyTabPanel();



  /**
   * constructor.
   */
  public AbstractI18nTextBoxBase()
  {
    this(false);
  }



  /**
   * constructor.
   * 
   * @param showSingleLanguage
   *          whether to show a panel if there is only a single language.
   */
  public AbstractI18nTextBoxBase(final boolean showSingleLanguage)
  {
    // add CSS for TabPanel
    panel.addStyleName(CssNames.I18N_TEXT_BOX_PANEL_CSS_STYLE);

    LocaleTabListener.getRegisteredLocale(new AsyncCallback<GwtLocale[]>()
    {

      public void onFailure(Throwable cause)
      {
        GwtHelper.error(new GwtI18nMessage(Messages.UNCAUGHT_EXCEPTION_ERROR_KEY, new String[] { cause.toString() }), cause);
      }



      public void onSuccess(GwtLocale[] locales)
      {
        for (final GwtLocale locale : locales) {
          Panel tabLabel = new SimplePanel();
          tabLabel.addStyleName(locale.getStringRepresentation());
          tabLabel.setTitle(locale.getDisplayLanguage());
          TextBoxBase tbb = createTextBoxBase();
          tbb.addChangeListener(changeListenerCollection);
          tbb.addClickListener(clickListenerCollection);
          tbb.addKeyboardListener(keyboardListenerCollection);
          tbb.addFocusListener(focusListenerCollection);
          widgetsByLocale.put(locale, tbb);
          Label label = new Label(locale.getDisplayLanguage());
          label.addStyleName(CssNames.I18N_TEXT_BOX_LABEL_CSS_STYLE);
          tabLabel.add(label);

          panel.add(tbb, tabLabel);
        }
        if (!showSingleLanguage && locales.length <= 1) {
          panel.getTabBar().setVisible(false);
        }
        panel.selectTab(LocaleTabListener.getSelectedLocale());
        synchronized (pendingCommands) {
          initialized = true;
          for (final Command command : pendingCommands) {
            command.execute();
          }
          pendingCommands.clear();
        }
      }

    });
    panel.addTabListener(LocaleTabListener.LOCALE_TAB_LISTENER);
    initWidget(panel);
  }



  /**
   * add change listener.
   * 
   * @param changeListener
   *          {@link ChangeListener}.
   */
  public void addChangeListener(ChangeListener changeListener)
  {
    changeListenerCollection.add(changeListener);
  }



  /**
   * add click listener.
   * 
   * @param clickListener
   *          {@link ClickListener}.
   */
  public void addClickListener(ClickListener clickListener)
  {
    clickListenerCollection.add(clickListener);
  }



  /**
   * add focus listener.
   * 
   * @param focusListener
   *          {@link FocusListener}.
   */
  public void addFocusListener(FocusListener focusListener)
  {
    focusListenerCollection.add(focusListener);
  }



  /**
   * add keyboard listener.
   * 
   * @param keyboardListener
   *          {@link KeyboardListener}.
   */
  public void addKeyboardListener(KeyboardListener keyboardListener)
  {
    keyboardListenerCollection.add(keyboardListener);
  }



  /**
   * create a {@link TextBoxBase}.
   * 
   * @return a {@link TextBoxBase}.
   */
  protected abstract TextBoxBase createTextBoxBase();



  /**
   * remove change listener.
   * 
   * @param changeListener
   *          {@link ChangeListener}.
   */
  public void removeChangeListener(ChangeListener changeListener)
  {
    changeListenerCollection.remove(changeListener);
  }



  /**
   * remove click listener.
   * 
   * @param clickListener
   *          {@link ClickListener}.
   */
  public void removeClickListener(ClickListener clickListener)
  {
    clickListenerCollection.remove(clickListener);
  }



  /**
   * remove focus listener.
   * 
   * @param focusListener
   *          {@link FocusListener}.
   */
  public void removeFocusListener(FocusListener focusListener)
  {
    focusListenerCollection.remove(focusListener);
  }



  /**
   * remove keyboard listener.
   * 
   * @param keyboardListener
   *          {@link keyboardListener}.
   */
  public void removeKeyboardListener(KeyboardListener keyboardListener)
  {
    keyboardListenerCollection.remove(keyboardListener);
  }



  /**
   * execute a {@link Command}.
   * 
   * @param command
   *          a {@link Command}.
   */
  public void executeCommand(Command command)
  {
    synchronized (pendingCommands) {
      if (initialized) {
        command.execute();
      }
      else {
        pendingCommands.add(command);
      }
    }
  }



  /**
   * getter for the widgetsByLocale property.
   * 
   * @return the widgetsByLocale
   */
  public Map<GwtLocale, TextBoxBase> getWidgetsByLocale()
  {
    return widgetsByLocale;
  }

}
